﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using Allinpay.Model;
using Newtonsoft.Json;


namespace Allinpay
{
    public class AllinpayLib
    {
        /// <summary>
        /// 写日志事件
        /// </summary>
        public static LogEventHandler LogEventHandler { get; set; }

        /// <summary>
        /// 触发LogEventHandler
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="content"></param>
        internal static void WriteLog(LogTagType tag, string content)
        {
            if (LogEventHandler != null)
            {
                LogEventHandler(tag, content);
            }
        }

        internal static Random Random = new Random();

        ///// <summary>
        ///// 返回错误码
        ///// </summary>
        //internal readonly static Dictionary<string, ErrorCodeInfo> ErrorCodeDict;
        //static AllinpayLib()
        //{
        //    ErrorCodeDict = new Dictionary<string, ErrorCodeInfo>()
        //    {
        //        {"9",new ErrorCodeInfo(){code="9",msg_cn="Action",msg_en = "Http",remark = "Not Allowed	HTTP方法被禁止	请用大写的POST或GET，如果有图片等信息传入则一定要用POST才可以"}},
        //        {"10",new ErrorCodeInfo(){code="10",msg_cn="Currently",msg_en = "Service",remark = "Unavailable	服务不可用	接口暂停或不能使用"}},
        //        {"11",new ErrorCodeInfo(){code="11",msg_cn="ISV",msg_en = "Insufficient",remark = "Permissions	合作伙伴权限不足 应用没有权限调用该接口的权限 "}},
        //        {"12",new ErrorCodeInfo(){code="12",msg_cn="Service",msg_en = "Remote",remark = "Error	远程服务出错	API调用后端服务出错"}},
        //        {"21",new ErrorCodeInfo(){code="21",msg_cn="Method",msg_en = "Missing",remark = "缺少方法名参数	传入的参数加入method字段"}},
        //        {"22",new ErrorCodeInfo(){code="22",msg_cn="Method",msg_en = "Invalid",remark = "不存在的方法名	传入的method字段必需是你所调用的API的名称，并且该API是确实存在的"}},
        //        {"23",new ErrorCodeInfo(){code="23",msg_cn="Format",msg_en = "Invalid",remark = "无效数据格式	传入的format必需为json或xml中的一种"}},
        //        {"24",new ErrorCodeInfo(){code="24",msg_cn="Signature",msg_en = "Missing",remark = "缺少签名参数	传入的参数中必需包含sign字段"}},
        //        {"25",new ErrorCodeInfo(){code="25",msg_cn="Signature",msg_en = "Invalid",remark = "无效签名	签名必需根据正确的算法算出来的"}},
        //        {"28",new ErrorCodeInfo(){code="28",msg_cn="App",msg_en = "Missing",remark = "Key	缺少AppKey参数	传入的参数必需包含app_key字段"}},
        //        {"29",new ErrorCodeInfo(){code="29",msg_cn="App",msg_en = "Invalid",remark = "Key	无效的AppKey参数	传入的参数必需包含正确的app_key字段"}},
        //        {"30",new ErrorCodeInfo(){code="30",msg_cn="Timestamp",msg_en = "Missing",remark = "缺少时间戳参数	传入的参数中必需包含timestamp参数"}},
        //        {"31",new ErrorCodeInfo(){code="31",msg_cn="Timestamp",msg_en = "Invalid",remark = "非法的时间戳参数	时间戳，格式为YYYYMMDDhhmmss，例如：20110101010101 "}},
        //        {"32",new ErrorCodeInfo(){code="32",msg_cn="Version",msg_en = "Missing",remark = "缺少版本参数	传入的参数中必需包含v字段"}},
        //        {"33",new ErrorCodeInfo(){code="33",msg_cn="Version",msg_en = "Invalid",remark = "非法的版本参数	用户传入的版本号格式错误，必需为数字格式, 用户传入的版本号没有被提供"}},
        //        {"34",new ErrorCodeInfo(){code="34",msg_cn="Signature",msg_en = "Missing",remark = "method	缺少加密方法	传入的参数中必需包含 sign_method 字段"}},
        //        {"35",new ErrorCodeInfo(){code="35",msg_cn="Signature",msg_en = "Invalid",remark = "method	非法的加密方法	传入的sign_method 必须为 md5，hmac，dsa中的一种"}},
        //        {"50",new ErrorCodeInfo(){code="50",msg_cn="缺少必选参数",msg_en = "missing-parameter",remark = "API文档中设置为必选的参数是必传的，请仔细核对文档"}},
        //        {"51",new ErrorCodeInfo(){code="51",msg_cn="parameter",msg_en = "invalid-",remark = "非法的参数	参数类型不对，例如：需要传入的是数字类型的，却传入了字符类型的参数"}},
        //        {"52",new ErrorCodeInfo(){code="52",msg_cn="参数错误",msg_en = "error-parameter",remark = "请求的参数经验证后不合法，如卡号不存在"}},
        //        {"53",new ErrorCodeInfo(){code="53",msg_cn="远程服务业务错误",msg_en = "business-error",remark = "一般是系统对正常业务处理的限制引起的，如卡密错误"}}
        //    };
        //}
        public static string AppKey
        {
            get { return GetAppSettingValue("allinpay.app_key"); }
        }

        /// <summary>
        /// 请求密钥
        /// </summary>
        public static string AppSecret
        {
            get { return GetAppSettingValue("allinpay.app_secret"); }
        }

        /// <summary>
        /// 响应密钥
        /// </summary>
        public static string AppResponseSecret
        {
            get { return GetAppSettingValue("allinpay.app_resonse_secret"); }
        }

        /// <summary>
        /// 平台接口地址
        /// </summary>
        public static string Url
        {
            get { return GetAppSettingValue("allinpay.url"); }
        }

        /// <summary>
        /// DES 数据密钥
        /// </summary>
        public static string DesSecret
        {
            get { return GetAppSettingValue("allinpay.dessecret"); }
        }

        /// <summary>
        /// 验签版本号
        /// </summary>
        public static string AppSignV
        {
            get { return GetAppSettingValue("allinpay.app_signv"); }
        }

        /// <summary>
        /// 商户号
        /// </summary>
        public static string CompanyNumber
        {
            get { return GetAppSettingValue("allinpay.company_number"); }
        }

        /// <summary>
        /// 支付活动
        /// </summary>
        public static string PaymentId
        {
            get { return GetAppSettingValue("allinpay.payment_id"); }
        }


        /// <summary>
        /// 签名（非必须参数，没有值不参与签名）
        /// </summary>
        /// <param name="dict"></param>
        /// <returns></returns>
        internal static string GetSign(Dictionary<string, string> dict)
        {
            var secret = AppSecret;
            var sign = secret + dict.Where(p => p.Value.IsNotNullAndWhiteSpace()).OrderBy(p => p.Key).Select(p => p.Key + p.Value).StringJoin(string.Empty) + secret;
            Log(sign);
            return sign.ToMD5(1);
        }

        #region DES加密

        /// <summary>
        /// DES加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string CrptoDESEncrypt(string str)
        {
            var key = DesSecret;
            byte[] inputByteArray = Encoding.UTF8.GetBytes(str);
            using (var des = new DESCryptoServiceProvider())
            {
                des.Mode = CipherMode.CBC;
                des.Padding = PaddingMode.PKCS7;
                des.IV = des.Key = Encoding.UTF8.GetBytes(key);
                using (var ms = new MemoryStream())
                using (var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(inputByteArray, 0, inputByteArray.Length);
                    cs.FlushFinalBlock();
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }

        #endregion

        internal static TResult Request<TRequest, TResult>(TRequest request)
            where TRequest : BaseParamInfo
        {
            request.format = "json";
            var dict = request.GetParamDict();
            var sign = GetSign(dict);
            Log(sign);
            request.sign = sign;
            dict.Add("sign", sign);
            var res = Request(dict.Select(p => p.Key + "=" + p.Value.UrlEncode()).StringJoin("&"), request.HttpMethod);
            Log(res);
            return res.ToDeserialize<TResult>();
        }

        /// <summary>
        /// 发起请求
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static string Request(string data, string method)
        {
            var urlReq = Url + "?" + data;
            WriteLog(LogTagType.RequestParams, urlReq);
            try
            {
                var content = !string.Equals(method, "post", StringComparison.OrdinalIgnoreCase) ? HttpGet(urlReq) : HttpPost(Url, data);
                WriteLog(LogTagType.ResultContent, content);
                return content;
            }
            catch (Exception e)
            {
                WriteLog(LogTagType.ResultContent, e.ToString());
                throw;
            }
        }

        private static string HttpGet(string url)
        {
            using (var webClient = new WebClient())
            {
                webClient.Encoding = Encoding.UTF8;
                return webClient.DownloadString(url);
            }
        }

        private static string HttpPost(string url, string data)
        {
            using (var webClient = new WebClient())
            {
                webClient.Encoding = Encoding.UTF8;
                webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                return webClient.UploadString(url, data);
            }
        }


        public static string GetAppSettingValue(string key)
        {
            return WebConfigurationManager.AppSettings[key];
        }

        [Conditional("DEBUG")]
        internal static void Log(object obj)
        {
             Console.WriteLine(JsonConvert.SerializeObject(obj));
        }

    }
}
